#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017-2018, Niklas Hauser
# Copyright (c) 2019, Raphael Lehmann
#
# This file is part of the modm project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# -----------------------------------------------------------------------------

from os.path import join, relpath, isdir, exists

def init(module):
    module.name = ":build:make"
    module.description = FileReader("module.md")


def prepare(module, options):
    module.add_option(
        BooleanOption(name="include_makefile", default=True,
                      description=descr_include_makefile))

    module.add_collector(
        CallableCollector(name="flag_format",
                          description="Formatting compile flags for Make"))

    return True


def build(env):
    def flag_format(flag):
        subs = {
            "target_base": "$(BUILDPATH)/$(MODM_PROJECT_NAME)",
            "project_source_dir": "$(CURDIR)",
            "gccpath": "$(GCC_BASE)",
        }
        if "{" in flag:
            return flag.format(**subs)
        return None
    env.collect("flag_format", flag_format)


def post_build(env):
    is_unittest = len(env["::unittest.source"])
    repositories = [p for p in env.buildlog.repositories if isdir(env.real_outpath(p, basepath="."))]
    repositories.sort(key=lambda name: "0" if name == "modm" else name)

    subs = env.query("::device")
    if subs["core"].startswith("cortex-m"):
        # get memory information
        subs["memories"] = env.query("::memories")
        subs["uf2mem"] = ["{:#x}:{:#x}:{}".format(m["start"], m["start"] + m["size"],
                          "CONTENTS" if "flash" in m["name"] else "NO_CONTENTS")
                          for m in subs["memories"]]
        subs["flash_offset"] = env.get(":platform:cortex-m:linkerscript.flash_offset", 0)
        subs["flash_address"] = hex(0x08000000 + subs["flash_offset"])
    else:
        subs["memories"] = []
    # Add SCons specific data
    linkerscript = env.get(":platform:cortex-m:linkerscript.override")
    linkerscript = linkerscript if linkerscript \
                   else "modm/link/linkerscript.ld"
    subs.update({
        "build_path": env.relcwdoutpath(env["::build.path"]),
        "modm_path": env.relcwdoutpath("modm"),
        "outpath": env.relcwdoutpath(""),
        "repositories": repositories,
        "linkerscript": linkerscript,
        "is_unittest": is_unittest,
        "program_extension": "exe" if env[":target"].identifier.family == "windows" else "elf",
    })
    if is_unittest:
        subs["unittest_source"] = env.relcwdoutpath(env["::unittest.source"])
    if len(env["::image.source"]):
        subs["image_source"] = env.relcwdoutpath(env["::image.source"])
    if subs["platform"] == "avr":
        subs.update(env.query("::avrdude_options"))
    if subs["platform"] == "sam":
        subs.update({"bossac_offset": env.get(":platform:cortex-m:linkerscript.flash_offset", None),
                     "bossac_options": " ".join(env.collector_values("::bossac.options"))})
    # Set these substitutions for all templates
    env.substitutions = subs

    sources = env.query("::source_files")
    def flags_format(flag):
        for fmt in env.collector_values("flag_format"):
            nflag = fmt(flag)
            if nflag: return nflag;
        return flag
    def relocate(path):
        return env.relcwdoutpath(path).replace(subs["modm_path"], "$(MODM_PATH)")

    for repo in repositories:
        files = []
        repo_filter = lambda scope: scope.repository == repo
        repo_flags = env.query("::collect_flags")(env, repo_filter)

        # Prepends -D to all CPP defines for this repo
        for filename, fileflags in repo_flags.items():
            for profile, flags in fileflags.get("cppdefines", {}).items():
                repo_flags[filename]["cppdefines"][profile] = ["-D"+f for f in flags]

        for f in sources[repo]:
            files.append( (f, repo_flags[f]) )

        include_paths = env.collector_values("::path.include", filterfunc=repo_filter)
        libary_paths = env.collector_values("::path.library", filterfunc=repo_filter)
        libaries = env.collector_values("::library", filterfunc=repo_filter)
        packages = env.collector_values("::pkg-config", filterfunc=repo_filter)

        subs.update({
            "repo": repo,
            "flags": repo_flags[None],
            "sources": files,
            "libraries": libaries,
            "library_paths": libary_paths,
            "include_paths": include_paths,
            "packages": packages,
            "is_modm": repo == "modm",
        })
        # Generate library SConscript
        env.outbasepath = repo
        env.template("resources/repo.mk.in", "repo.mk",
                     filters={"flags_format": flags_format, "relocate": relocate,
                              "objectify": lambda f: f.rsplit(".", 1)[0]+".o"})

    env.outbasepath = "modm"
    env.template("resources/config.mk.in", "config.mk", filters={"relocate": relocate})
    env.template("resources/compiler.mk.in", "compiler.mk")

    # these are the ONLY files that are allowed to NOT be namespaced with modm!
    env.outbasepath = env.cwdpath(".")
    if env["include_makefile"]:
        env.template("resources/Makefile.in", "Makefile", filters={"relocate": relocate})


# ============================ Option Descriptions ============================
descr_include_makefile = """# Generate a Makefile

!!! warning "This overwrites any top-level `Makefile`!"
"""
